/******************************************************
 * @file render.c
 * @author Destiny 
 * @brief 功能函数集
 * @version 0.1
 * @date 2024-07-19
 *****************************************************/
/* Includes ------------------------------------------------------------------*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* Private Includes ----------------------------------------------------------*/
#include "config.h"
#include "render.h"
#include "file.h"
#include "page_manager.h"
#include "fonts_manager.h"
#include "encoding_manager.h"
#include "picfmt_manager.h"
/* Private typedef -----------------------------------------------------------*/

/* Private Define ------------------------------------------------------------*/

/* Private Function Prototypes -----------------------------------------------*/

/**
 * @brief 要显示的字符是否完全在指定矩形区域内
 * 
 * @param iTopLeftX 区域
 * @param iTopLeftY 
 * @param iBotRightX 
 * @param iBotRightY 
 * @param ptFontBitMap 字体数据
 * @return int 1：在;0：不再
*/
static int isFontInArea(int iTopLeftX, int iTopLeftY, int iBotRightX, int iBotRightY, PT_FontBitMap ptFontBitMap)
{
    if ((ptFontBitMap->iXLeft >= iTopLeftX) && (ptFontBitMap->iXMax <= iBotRightX) && \
         (ptFontBitMap->iYTop >= iTopLeftY) && (ptFontBitMap->iYMax <= iBotRightY))
         return 1;
    else
        return 0;
        
}

/**
 * @brief 根据位图中的数据把字符显示到videomem中
 * 
 * @param ptFontBitMap 字体位图数据
 * @param ptVideoMem   显示缓冲区
 * @return int 成功：0;失败：其他
*/
static int MergeOneFontToVideoMem(PT_FontBitMap ptFontBitMap, PT_VideoMem ptVideoMem)
{
	int i = 0;
	int x, y;
	int bit;
	int iNum;
	uint8_t ucByte;

	if (ptFontBitMap->iBpp == 1)
	{
		for (y = ptFontBitMap->iYTop; y < ptFontBitMap->iYMax; y++)
		{
			/* 当前位图行第一个字节索引 */
			i = (y - ptFontBitMap->iYTop) * ptFontBitMap->iPitch;
			/* 打印一行 */
			for (x = ptFontBitMap->iXLeft, bit = 7; x < ptFontBitMap->iXMax; x++)
			{
				if (bit == 7)
				{
					ucByte = ptFontBitMap->pucBuffer[i++];
				}
				
				if (ucByte & (1<<bit))
				{
					iNum = SetColorForPixelInVideoMem(x, y, ptVideoMem, g_FontsColor);
				}
				else
				{			
					iNum = SetColorForPixelInVideoMem(x, y, ptVideoMem, g_BackGroundColor);
				}
				if (iNum == -1)
				{
					return -1;
				}
				bit--;
				if (bit == -1)
				{
					bit = 7;
				}
			}
		}
	}
	else if (ptFontBitMap->iBpp == 8)
	{
		for (y = ptFontBitMap->iYTop; y < ptFontBitMap->iYMax; y++)
			for (x = ptFontBitMap->iXLeft; x < ptFontBitMap->iXMax; x++)
			{
				
				if (ptFontBitMap->pucBuffer[i++])
				{
					iNum = SetColorForPixelInVideoMem(x, y, ptVideoMem, g_FontsColor);
				}
				else
				{
					iNum = SetColorForPixelInVideoMem(x, y, ptVideoMem, g_BackGroundColor);
				}
				
				if (iNum == -1)
				{
					return -1;
				}
			}
	}
	else
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"ShowOneFont error, can't support %d bpp\n", ptFontBitMap->iBpp);
		return -1;
	}
	return 0;
}

/**
 * @brief 计算文字位置
 * 
 * @param ptFontBitMap 字体位图数据
 * @param iStartX 	   显示起始位置X坐标 
 * @param iYres 	   显示区域Y最大值
 * @param iXres 	   显示区域X最大值
 * @param iFontSize    字体大小
 * @return int 		   成功：0;失败：其他
*/
static int RelocateFontPos(PT_FontBitMap ptFontBitMap, int iStartX,int iYres,int iXres,int iFontSize)
{
	int iLcdY;
	int iDeltaX;
	int iDeltaY;

	if (ptFontBitMap->iYMax > iYres)
	{
		/* 满页了 */
		return -1;
	}

	/* 超过LCD最右边,换行操作 */
	if (ptFontBitMap->iXMax > iXres)
	{
		iLcdY = ptFontBitMap->iCurOriginY +iFontSize;
		if ((ptFontBitMap->iYMax+iFontSize) > iYres)
		{
			/* 满页了 */
			return -1;
		}
		else
		{
			/* 没满页 */
			iDeltaX = 0 - ptFontBitMap->iCurOriginX + iStartX;
			iDeltaY = iLcdY - ptFontBitMap->iCurOriginY;

			ptFontBitMap->iCurOriginX  += iDeltaX;
			ptFontBitMap->iCurOriginY  += iDeltaY;

			ptFontBitMap->iNextOriginX += iDeltaX;
			ptFontBitMap->iNextOriginY += iDeltaY;

			ptFontBitMap->iXLeft += iDeltaX;
			ptFontBitMap->iXMax  += iDeltaX;

			ptFontBitMap->iYTop  += iDeltaY;
			ptFontBitMap->iYMax  += iDeltaY;
		}
	}
	
	return 0;
}
/* Public Function Prototypes ------------------------------------------------*/

/**
 * @brief 显示缓冲区数据刷入显示设备
 * 
 * @param ptVideoMem 显示缓冲区
*/
void FlushVideoMemToDev(PT_VideoMem ptVideoMem)
{
	
	if (!ptVideoMem->bDevFrameBuffer)
	{
		GetDefaultDispDev()->ShowPage(ptVideoMem);
	}
}

/**
 * @brief 判断本程序能否支持该图片文件,目前只能支持BMP/JPG格式的文件
 * 
 * @param strFileName 图片路径
 * @return int 支持：1;不支持：0;
*/
int isPictureFileSupported(char *strFileName)
{
	T_FileMap tFileMap;
	int iError;

	strncpy(tFileMap.strFileName, strFileName, 256);
	tFileMap.strFileName[255] = '\0';
    iError = MapFile(&tFileMap);
    if (iError)
    {
        DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"MapFile %s error!\n", strFileName);
        return 0;
    }

    if (GetParser(&tFileMap) == NULL)
    {
        UnMapFile(&tFileMap);
        return 0;
    }

    UnMapFile(&tFileMap);
    return 1;
}

/**
 * @brief 从图片文件中取出象素数据
 * 
 * @param strFileName 图片路径
 * @param ptPixelDatas 图片数据
 * @return int 成功：0;失败：其他
*/
int GetPixelDatasFrmFile(char *strFileName, PT_PixelDatas ptPixelDatas)
{
	T_FileMap tFileMap;
	int iError;
	int iXres, iYres, iBpp;
    PT_PicFileParser ptParser;

	strncpy(tFileMap.strFileName, strFileName, 256);
	tFileMap.strFileName[255] = '\0';
	
	iError = MapFile(&tFileMap);
	if (iError)
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"MapFile %s error!\n", strFileName);
		return -1;
	}

    ptParser = GetParser(&tFileMap);
	if (ptParser == NULL)
	{
        UnMapFile(&tFileMap);
		return -1;
	}

	GetDispResolution(&iXres, &iYres, &iBpp);
	ptPixelDatas->iBpp = iBpp;
	iError = ptParser->GetPixelDatas(&tFileMap, ptPixelDatas);
	if (iError)
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"GetPixelDatas for %s error!\n", strFileName);
        UnMapFile(&tFileMap);
		return -1;
	}

    UnMapFile(&tFileMap);
	return 0;
}

/**
 * @brief 释放图像数据所占缓冲区
 * 
 * @param ptPixelDatas 图像数据结构体
*/
void FreePixelDatasFrmFile(PT_PixelDatas ptPixelDatas)
{
	free(ptPixelDatas->aucPixelDatas);
}

/**
 * @brief 设置VideoMem中某个座标象素的颜色
 * 
 * @param iX X坐标
 * @param iY Y坐标
 * @param ptVideoMem 显示缓冲区
 * @param dwColor 颜色数据
 * @return int 成功 颜色字节数;失败 -1
*/
int SetColorForPixelInVideoMem(int iX, int iY, PT_VideoMem ptVideoMem, uint32_t dwColor)
{
	uint8_t *pucVideoMem;
	uint16_t *pwVideoMem16bpp;
	uint32_t *pdwVideoMem32bpp;
	uint16_t wColor16bpp; 
	int iRed;
	int iGreen;
	int iBlue;

	pucVideoMem      = ptVideoMem->tPixelDatas.aucPixelDatas;
	pucVideoMem      += iY * ptVideoMem->tPixelDatas.iLineBytes + iX * ptVideoMem->tPixelDatas.iBpp / 8;
	pwVideoMem16bpp  = (uint16_t *)pucVideoMem;
	pdwVideoMem32bpp = (uint32_t *)pucVideoMem;

	switch (ptVideoMem->tPixelDatas.iBpp)
	{
		case 8:
		{
			*pucVideoMem = (uint8_t)dwColor;
			return 1;
		}
		case 16:
		{
			iRed   = (dwColor >> (16+3)) & 0x1f;
			iGreen = (dwColor >> (8+2)) & 0x3f;
			iBlue  = (dwColor >> 3) & 0x1f;
			wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue;
			*pwVideoMem16bpp	= wColor16bpp;
			return 2;
		}
		case 32:
		{
			*pdwVideoMem32bpp = dwColor;
			return 4;
		}
		default :
		{			
			return -1;
		}
	}
	return -1;
}

/**
 * @brief 将VideoMem中某个矩形区域设为某颜色
 * 
 * @param iTopLeftX 区域坐标
 * @param iTopLeftY 
 * @param iBotRightX 
 * @param iBotRightY 
 * @param ptVideoMem 显示缓冲区
 * @param dwColor 颜色
*/
void ClearRectangleInVideoMem(int iTopLeftX, int iTopLeftY, int iBotRightX, int iBotRightY, PT_VideoMem ptVideoMem, uint32_t dwColor)
{
	int x, y;
	for (y = iTopLeftY; y <= iBotRightY; y++)
		for (x = iTopLeftX; x <= iBotRightX; x++)
			SetColorForPixelInVideoMem(x, y, ptVideoMem, dwColor);
}

/**
 * @brief 在VideoMem的指定矩形居中显示字符串
 * 
 * @param iTopLeftX 矩形斜对角的坐标（顶部）
 * @param iTopLeftY 
 * @param iBotRightX 矩形斜对角的坐标（底部）
 * @param iBotRightY 
 * @param pucTextString 需显示的字符
 * @param ptVideoMem  缓冲区
 * @return int 成功：0;失败：其他
*/
int MergerStringToCenterOfRectangleInVideoMem(int iTopLeftX, int iTopLeftY, int iBotRightX, int iBotRightY, uint8_t *pucTextString, PT_VideoMem ptVideoMem)
{
	int iLen;
	int iError;
	uint8_t *pucBufStart;
	uint8_t *pucBufEnd;
	uint32_t dwCode;
	T_FontBitMap tFontBitMap;
	
	int bHasGetCode = 0;

	int iMinX = 32000, iMaxX = -1;
	int iMinY = 32000, iMaxY = -1;

	int iStrTopLeftX , iStrTopLeftY;

	int iWidth, iHeight;

	tFontBitMap.iCurOriginX = 0;
	tFontBitMap.iCurOriginY = 0;
	pucBufStart = pucTextString;
	pucBufEnd   = pucTextString + strlen((char *)pucTextString);   

	
	ClearRectangleInVideoMem(iTopLeftX, iTopLeftY, iBotRightX, iBotRightY, ptVideoMem, g_BackGroundColor);
	
	
	while (1)
	{
		
		iLen = GetCodeFrmBuf(pucBufStart, pucBufEnd, &dwCode);
		if (0 == iLen)
		{
			
			if (!bHasGetCode)
			{
				
				return -1;
			}
			else
			{
				break;
			}
		}

		bHasGetCode = 1;
		pucBufStart += iLen;

		
		iError = GetFontBitmap(dwCode, &tFontBitMap);
		if (0 == iError)
		{									
			if (iMinX > tFontBitMap.iXLeft)
			{
				iMinX = tFontBitMap.iXLeft;
			}
			if (iMaxX < tFontBitMap.iXMax)
			{
				iMaxX = tFontBitMap.iXMax;
			}

			if (iMinY > tFontBitMap.iYTop)
			{
				iMinY = tFontBitMap.iYTop;
			}
			if (iMaxY < tFontBitMap.iXMax)
			{
				iMaxY = tFontBitMap.iYMax;
			}
			
			tFontBitMap.iCurOriginX = tFontBitMap.iNextOriginX;
			tFontBitMap.iCurOriginY = tFontBitMap.iNextOriginY;
		}
		else
		{
			DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"GetFontBitmap for calc width/height error!\n");
		}
	}	

	iWidth  = iMaxX - iMinX;
	iHeight = iMaxY - iMinY;

    
    if (iWidth > iBotRightX - iTopLeftX)
    {
        iWidth = iBotRightX - iTopLeftX;
    }

    
	if (iHeight > iBotRightY - iTopLeftY)
	{
		DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
		
		return -1;
	}
	

	
	iStrTopLeftX = iTopLeftX + (iBotRightX - iTopLeftX - iWidth) / 2;
	iStrTopLeftY = iTopLeftY + (iBotRightY - iTopLeftY - iHeight) / 2;
	

	
	tFontBitMap.iCurOriginX = iStrTopLeftX - iMinX;
	tFontBitMap.iCurOriginY = iStrTopLeftY - iMinY;

	
	
	pucBufStart = pucTextString;	
	bHasGetCode = 0;
	while (1)
	{
		
		iLen = GetCodeFrmBuf(pucBufStart, pucBufEnd, &dwCode);
		if (0 == iLen)
		{
			
			if (!bHasGetCode)
			{
				DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);
				return -1;
			}
			else
			{
				break;
			}
		}

		bHasGetCode = 1;
		pucBufStart += iLen;

		
		iError = GetFontBitmap(dwCode, &tFontBitMap);
		if (0 == iError)
		{
			
			
            if (isFontInArea(iTopLeftX, iTopLeftY, iBotRightX, iBotRightY, &tFontBitMap))
            {
    			if (MergeOneFontToVideoMem(&tFontBitMap, ptVideoMem))
    			{
    				DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"MergeOneFontToVideoMem error for code 0x%x\n", dwCode);
    				return -1;
    			}
            }
            else
            {
                return 0;
            }
			
			
			tFontBitMap.iCurOriginX = tFontBitMap.iNextOriginX;
			tFontBitMap.iCurOriginY = tFontBitMap.iNextOriginY;
		}
		else
		{
			DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"GetFontBitmap for drawing error!\n");
		}
	}

	return 0;
}

/**
 * @brief 把显示设备上指定区域里每个象素的颜色取反
 * 
 * @param ptLayout 区域信息
 * @param ptPageVideoMem 显示缓冲区
*/
void InvertButton(PT_Layout ptLayout,PT_VideoMem ptPageVideoMem)
{
	int iY;
	int i;
	int iButtonWidthBytes;
	uint8_t *pucVideoMem;
	PT_DispOpr ptDispOpr = GetDefaultDispDev();

	// pucVideoMem = ptDispOpr->pucDispMem;
	pucVideoMem = ptPageVideoMem->tPixelDatas.aucPixelDatas;
	pucVideoMem += ptLayout->iTopLeftY * ptDispOpr->iLineWidth + ptLayout->iTopLeftX * ptDispOpr->iBpp / 8; 
	iButtonWidthBytes = (ptLayout->iBotRightX - ptLayout->iTopLeftX + 1) * ptDispOpr->iBpp / 8;

	for (iY = ptLayout->iTopLeftY; iY <= ptLayout->iBotRightY; iY++)
	{
		for (i = 0; i < iButtonWidthBytes; i++)
		{
			pucVideoMem[i] = ~pucVideoMem[i];  
		}
		pucVideoMem += ptDispOpr->iLineWidth;
	}
}

/**
 * @brief从TXT文件中读取数据，并写入显示缓冲区
 * 
 * @param ptVideoMem 显示缓冲区
 * @param ptPageLayout 页面布局信息
 * @param pucCurPagePos 指向当前页第一个字符，若为NULL，表示第一次调用该函数
 * @param pucFileEnd   	文件尾部地址
 * @param iFontSize     字体大小
 * @return uint8_t* 指针,指向下一页第一个字符
*/
uint8_t * GetOnePageFrmTXT(PT_VideoMem ptVideoMem,PT_PageLayout ptPageLayout,uint8_t *pucCurPagePos,uint8_t *pucFileEnd,int iFontSize)
{
	T_FontBitMap tFontBitMap;		//字体位图数据
	int iError;
	int iXres, iYres, iBpp;
	uint32_t  puiCode;
	int iLen,bHasGetCode = 0;
	int iStartX = ptPageLayout->iTopLeftX + iFontSize/2;  //显示起始位置X坐标
	uint8_t *pucFilePos = pucCurPagePos;   //文件当前指针

	ClearVideoMemRegion(ptVideoMem,ptPageLayout,g_BackGroundColor);
	GetDispResolution(&iXres, &iYres, &iBpp);
	
	/*确定第一个字符原点*/
	tFontBitMap.iCurOriginX = iStartX;
	tFontBitMap.iCurOriginY = iFontSize;
	
	SetFontSize(iFontSize);
	while(1)
	{
		/* 解码文件，得到对应unicode编码 */
		iLen = GetCodeFrmBuf(pucFilePos, pucFileEnd, &puiCode);
		
		if (0 == iLen)	/* 未读取到数据 */
		{
			
			if (!bHasGetCode)  //出错
			{
				
				return (void *)-1;
			}
			else           //读完
			{
				break;
			}
		}

		bHasGetCode = 1;
		pucFilePos += iLen;
		if (puiCode == '\n')
		{
			/* 回车换行 */
			tFontBitMap.iCurOriginX = iStartX;
			tFontBitMap.iCurOriginY += iFontSize;
			//tFontBitMap.iYMax       += iFontSize;
			/*显示完一页*/
			if(tFontBitMap.iYMax > iYres)
			{
				pucFilePos -=iLen;
				break;
			}
			else
			{
				continue;
			}
		}
		else if (puiCode == '\r')
		{
			continue;
		}
		else if (puiCode == '\t')
		{
			/* TAB键用一个空格代替 */
			puiCode = ' ';
		}
		/* 获取字体位图数据 */
		iError = GetFontBitmap(puiCode, &tFontBitMap);
		if(iError < 0)
		{
			return (void *)-1;
		}

		/* 计算当前字显示位置 */
		if(RelocateFontPos(&tFontBitMap,iStartX,iYres,iXres,iFontSize) < 0)
		{
			pucFilePos -=iLen;
			break;
		}
		MergeOneFontToVideoMem(&tFontBitMap,ptVideoMem);

		tFontBitMap.iCurOriginX = tFontBitMap.iNextOriginX;
	}
	return pucFilePos;
}

/**
 * @brief 读取文件图标图像数据
 * 
 * @param ptPixelDatas 图标图像数据信息
 * @param strFilePath  图标路径
 * @param iMaxTotalBytes 图标总字节数
 * @param iHeight 图标布局高度
 * @param iWidth  图标布局宽图
 * @param iBpp    图标像素位数
 * @return int    成功：0;失败：其他
*/
int GetIcons(PT_PixelDatas ptPixelDatas,char *strFilePath,int iMaxTotalBytes,int iHeight,int iWidth,int iBpp) 
{
    int iError;
    T_PixelDatas tOriginIconPixelDatas;
    ptPixelDatas->iBpp = iBpp;
    ptPixelDatas->aucPixelDatas = malloc(iMaxTotalBytes);
    if (ptPixelDatas->aucPixelDatas == NULL)
    {
        return -1;
    }
    iError = GetPixelDatasFrmFile(strFilePath, &tOriginIconPixelDatas);
    if (iError)
    {
        DBG_PRINTF(DLOG_LVL_ERROR,DLOG_TAG,"GetPixelDatasFrmFile %s error!\n", strFilePath);
        return -2;
    }
    ptPixelDatas->iHeight = iHeight;
    ptPixelDatas->iWidth = iWidth;
    ptPixelDatas->iLineBytes = iWidth * iBpp / 8;
    ptPixelDatas->iTotalBytes =  ptPixelDatas->iLineBytes * iHeight;
    PicZoom(&tOriginIconPixelDatas, ptPixelDatas);
    FreePixelDatasFrmFile(&tOriginIconPixelDatas);
    return 0;
}